package com.lgl.scs.wx.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.lgl.scsapi.base.dao.DaoSupport;
import com.lgl.scsapi.system.model.AppUser;
import com.lgl.scsapi.system.model.PageData;
import com.lgl.scsapi.util.utils.BillNumberTool;
import com.lgl.scsapi.util.utils.Constants;
import com.lgl.scsapi.util.utils.Tools;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@Slf4j
@Service("wxUserService")
public class WxUserService {
	@Resource(name = "daoSupport")
	private DaoSupport dao;
	@Resource
	private StringRedisTemplate stringRedisTemplate;
	@Autowired
	private RestTemplate restTemplate;

	/**
	 * 通过用户名和密码查找用户
	 * @param pd 用户名和密码参数
	 * @return AppUser对象
	 */
	public AppUser findByNameAndPassword(PageData pd) {
		return (AppUser) dao.findForObject("WxUserMapper.findByNameAndPassword", pd);
	}

	/**
	 * 通过手机号码获取用户
	 * @param phone 手机号
	 * @return AppUser对象
	 */
	public AppUser findByPhone(String phone) {
		return (AppUser) dao.findForObject("WxUserMapper.findByPhone", phone);
	}
	/**
	 * 获取当前登录用户其它信息
	 * @param pd 查询的参数
     * @param appUser 当前登录用户
	 * @return 当前登录用户其它信息
	 */
	public PageData getMyOtherInfoByName(PageData pd, AppUser appUser) {
		pd.put("USERNAME", appUser.getUserName());
		return (PageData) dao.findForObject("WxUserMapper.getMyOtherInfoByName", pd);
	}

	/**
	 * 编辑用户信息
	 * @param pd 修改的用户参数
	 * @param appUser 当前登录用户
	 * @return 成功修改的行数
	 */
	public int editMyOtherInfo(PageData pd,AppUser appUser) {
		String oldPwd = (String) pd.remove("oldPwd"), newPwd = (String) pd.remove("newPwd");
		//修改密码，登录后通过我的页面作的修改
		if (StringUtils.hasText(oldPwd) && StringUtils.hasText(newPwd)) {
			oldPwd = Tools.encryptPassword(oldPwd);
			newPwd = Tools.encryptPassword(newPwd);
			pd.clear();
			pd.put("oldPwd", oldPwd);
			pd.put("newPwd", newPwd);
		}
		pd.put("userName", appUser.getUserName());
		return editMyOtherInfo(pd);
	}

	/**
	 * 修改我的信息
	 * @param editPageData 要修改的参数和条件
	 * @return 更新的行数
	 */
	public int editMyOtherInfo(PageData editPageData) {
		return dao.update("WxUserMapper.editMyOtherInfo", editPageData);
	}
	/**
	 * 获取微信用户登录的session key和openid
	 * @param jsCode 前台登录微信返回的登录编码
	 * @return 微信用户登录的session key和openid
	 */
	public String getWxSessionKeyAndOpenId(String jsCode){
		StringBuilder openIdUrl = new StringBuilder(Constants.WX_MINI_GET_USER_INFO_URL);
		openIdUrl.append("?grant_type=authorization_code&appid=").append(Constants.WX_MINI_APP_ID)
				.append("&secret=").append(Constants.WX_MINI_APP_SECRET).append("&js_code=").append(jsCode);
		return restTemplate.getForObject(openIdUrl.toString(), String.class);
	}
	/**
	 * 获取微信用户的openid
	 * @param jsCode 前台登录微信返回的登录编码
	 * @return 微信用户的openid
	 */
	public String getWxOpenId(String jsCode){
		String openIdSessionKey = getWxSessionKeyAndOpenId(jsCode);
		JSONObject openJsonObject = JSON.parseObject(openIdSessionKey);
		return openJsonObject.getString("openid");
	}

	/**
	 * 保存用户
	 * @param wxUser 要保存的用户对象
	 */
	public void saveWxUser(AppUser wxUser) {
		dao.save("WxUserMapper.saveWxUser", wxUser);
	}
	/**
	 * 通过验证码注册，并绑定用户微信openid
	 * @param pd 注册参数，手机号，验证码，密码
	 * @return 成功或失败标识，及成功后的用户对象
	 */
	public PageData registerByCheckCode(PageData pd){
		String checkCode = (String) pd.remove("code"), phone = pd.getString("phone");
		String phoneKey = Constants.PHONE_CHECK_CODE_K_PRE + phone;
		//从redis中取验证码，并与传过来的比较
		String redisCheckCode = stringRedisTemplate.opsForValue().get(phoneKey);
		if(StringUtils.isEmpty(phone)){
			pd.clear();
			pd.put("status", "6");
			return pd;
		}
		//验证码已过期
		if (StringUtils.isEmpty(redisCheckCode)){
			pd.clear();
			pd.put("status", "1");
			return pd;
		}
		//验证码不正确
		if (!StringUtils.hasText(checkCode) || !checkCode.equals(redisCheckCode)) {
			pd.clear();
			pd.put("status", "7");
			return pd;
		}
		//用完了立刻删除，不要等自动过期，防止redis缓存被无用的数据占用
		stringRedisTemplate.delete(phoneKey);
		AppUser	appUser = findByPhone(phone);
		if(appUser != null) {
			pd.put("status", "2");
			pd.remove("password");
			return pd;
		}

		String password = pd.getString("password");
		password = Tools.encryptPassword(password);
		String userId = BillNumberTool.getRegisterUserId(stringRedisTemplate,0);
		pd.clear();
		pd.put("status", "S");
        appUser = new AppUser();
        appUser.setUserId(userId).setUserName(phone).setPhone(phone).setPassword(password);
		saveWxUser(appUser);
		pd.put("appUser", appUser);
		return pd;
	}
	/**
	 * 通过openid获取用户
	 * @param pd openid参数
	 * @return AppUser对象
	 */
	public AppUser findByOpenId(PageData pd) {
		return (AppUser) dao.findForObject("WxUserMapper.findByOpenId", pd);
	}
	/**
	 * 通过openid获取用户
	 * @param pd userId参数
	 * @return AppUser对象
	 */
	public AppUser findById(PageData pd) {
		return (AppUser) dao.findForObject("WxUserMapper.findById", pd);
	}
    /**
     * 修改我的手机号码
     * @param appUser 当前登录用户
     * @param mpPd 手机、验证码等参数
     * @return 成功或失败的标识，0：成功，其它值：失败
     */
    public String modifyMyPhone(AppUser appUser, PageData mpPd) throws Exception {
	    String newPhone = mpPd.getString("phone"),oldPhone = appUser.getPhone();
	    String checkResult = checkModifyBind(mpPd,oldPhone,newPhone);
	    if (StringUtils.hasText(checkResult)){
	    	return checkResult;
		}
		AppUser phoneUser = findByPhone(newPhone);
		//如果phoneUser存在，则说明该手机号已被绑定
		if (phoneUser != null){
			return "8";
		}
		return setUpdateLoginUser(appUser,mpPd,oldPhone,newPhone,appUser);
    }
    /**
     * 绑定我的手机号码
     * @param appUser 当前登录用户
     * @param mpPd 手机、验证码等参数
     * @return 成功或失败的标识，0：成功，其它值：失败
     */
	@Transactional(value = "scsTxManager",propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ,rollbackFor = Exception.class)
    public PageData bindMyPhone(AppUser appUser, PageData mpPd) throws Exception {
		if (appUser == null){
			mpPd.clear();
			mpPd.put("status","-1");
			return mpPd;
		}
		String userPhone = mpPd.getString("phone");
        String checkResult = checkModifyBind(mpPd,userPhone,userPhone);
        //如果校验不通过
        if (StringUtils.hasText(checkResult)){
        	mpPd.clear();
        	mpPd.put("status",checkResult);
            return mpPd;
        }
		AppUser phoneUser = findByPhone(userPhone);
		//如果phoneUser不存在，则说明该手机号未注册或未绑定
		if (phoneUser == null){
			checkResult = setUpdateLoginUser(appUser,mpPd,userPhone,userPhone,appUser);
			mpPd.put("status",checkResult);
			return mpPd;
		}
		//如果登录用户ID=要绑定的手机号码用户，则说明已经绑定成功了
		if (appUser.getUserId().equals(phoneUser.getUserId())){
			mpPd.clear();
			mpPd.put("status","0");
			mpPd.put("userId", appUser.getUserId());
			return mpPd;
		}
		PageData wxPd = new PageData(1);
		wxPd.put("userId",appUser.getUserId());
		AppUser wxUser = findById(wxPd);
		String openId = wxUser.getOpenId();
		int row = dao.delete("WxUserMapper.deleteUserByOpenId", openId);
		if (row < 1){
			log.error("删除用户失败,phoneUser:" + phoneUser.toString() + "\nappUser:" + appUser.toString());
			mpPd.clear();
			mpPd.put("status","2");
			return mpPd;
		}
		mpPd.put("editOpenId",openId);
		checkResult = setUpdateLoginUser(appUser,mpPd,userPhone,userPhone,phoneUser);
		mpPd.clear();
		mpPd.put("status",checkResult);
		if ("0".equals(checkResult)){
			mpPd.put("userId", phoneUser.getUserId());
		}
		return mpPd;
    }

    /**
     * 绑定手机号/修改手机号 的校验方法
     * @param mpPd 查询用户的参数
     * @param oldPhone 原来的手机号码/要绑定的手机号码
	 * @param newPhone 新的手机号码/要绑定的手机号码
     * @return 成功或失败的标识，null:校验成功，not null:校验失败，返回错误标识
     */
    private String checkModifyBind(PageData mpPd,String oldPhone,String newPhone){
        if (!StringUtils.hasText(newPhone)) {
            return "6";
        }
        String checkCode = (String) mpPd.remove("checkCode");
        if (!StringUtils.hasText(checkCode)) {
            return "4";
        }
        String redisCheckCode = stringRedisTemplate.opsForValue().get(Constants.PHONE_CHECK_CODE_K_PRE + oldPhone);
        if (!StringUtils.hasText(checkCode)) {
            return "7";
        }
        if (!checkCode.equals(redisCheckCode)) {
            return "5";
        }
        return null;
    }

	/**
	 * 设置登录用户
	 * @param appUser 当前登录用户
	 * @param mpPd 修改用户的参数
	 * @param oldPhone 用户原来的手机号
	 * @param newPhone 用户新的手机号
	 * @param newUser 新的用户对象
	 * @return 返回成功标识
	 */
	public String setUpdateLoginUser(AppUser appUser,PageData mpPd,String oldPhone, String newPhone,AppUser newUser) throws Exception {
		mpPd.put("editUserName", newPhone);
		mpPd.put("userName",newUser.getUserName());
		int row = editMyOtherInfo(mpPd);
		if (row < 1) {
			throw new Exception("修改我的信息失败[setUpdateLoginUser],mpPd=" + mpPd);
		}
		stringRedisTemplate.delete(Constants.PHONE_CHECK_CODE_K_PRE + oldPhone);
		appUser.setPhone(newPhone);
		appUser.setUserName(newPhone);
		appUser.setUserId(newUser.getUserId());
		SecurityUtils.getSubject().getSession().setAttribute(Constants.SESSION_USER, appUser);
		return "0";
	}

    /**
     * 通过原密码重置新密码
     * @param loginUser 当前登录人
     * @param resetPd 重置的新密码与原密码参数
     * @return 成功或失败对象
     */
	public PageData resetPwdByOldPwd(AppUser loginUser,PageData resetPd){
        if (loginUser == null) {
            resetPd.clear();
            resetPd.put("status", "-1");
            return resetPd;
        }
        String newPwd = (String) resetPd.remove("newPwd"), oldPwd = (String) resetPd.remove("oldPwd");
        if (!StringUtils.hasText(newPwd) || !StringUtils.hasText(oldPwd)) {
            resetPd.clear();
            resetPd.put("status", !StringUtils.hasText(newPwd) ? "3" : "4");
            return resetPd;
        }
        oldPwd = new SimpleHash(Constants.ALGORITHM_NAME,oldPwd,Constants.SHIRO_SALT,Constants.HASH_ITERATIONS).toHex();
        String userPwd = loginUser.getPassword();
        //如果密码不为空，且原密码不为初始密码，用户密码不等于原密码，则说明前台传的原密码错误
        if (StringUtils.hasText(userPwd) && !Constants.USER_DEFAULT_PASSWORD.equals(oldPwd) && !oldPwd.equals(userPwd)){
			resetPd.clear();
			resetPd.put("status", "5");
			return resetPd;
		}
        newPwd = new SimpleHash(Constants.ALGORITHM_NAME,newPwd,Constants.SHIRO_SALT,Constants.HASH_ITERATIONS).toHex();
        resetPd.put("newPwd", newPwd);
        resetPd.put("oldPwd", oldPwd);
        resetPd.put("userName", loginUser.getUserName());
        int row = editMyOtherInfo(resetPd);
        resetPd.clear();
        boolean editSuccess = row > 0;
        if (editSuccess){
        	loginUser.setPassword(newPwd);
			SecurityUtils.getSubject().getSession().setAttribute(Constants.SESSION_USER, loginUser);
		}
        resetPd.put("status", editSuccess ? "0" : "2");
        return resetPd;
    }

	/**
	 * 通过验证码重置密码
	 * @param resetPwdPd 重置参数：手机号、新密码、验证码
	 * @return 包含成功或失败标识的PageData对象
	 */
	public PageData resetPwdByCheckCode(PageData resetPwdPd) {
		String phone = (String) resetPwdPd.remove("phone"), newPwd = (String) resetPwdPd.remove("newPwd"),
				checkCode = (String) resetPwdPd.remove("checkCode");
		if (!StringUtils.hasText(phone) || !Tools.checkMobileNumber(phone)){
			resetPwdPd.put("status", "6");
			return resetPwdPd;
		}
		if (!StringUtils.hasText(newPwd)) {
			resetPwdPd.put("status", "3");
			return resetPwdPd;
		}
		if (!StringUtils.hasText(checkCode)) {
			resetPwdPd.put("status", "4");
			return resetPwdPd;
		}
		String phoneKey = Constants.PHONE_CHECK_CODE_K_PRE + phone;
		//从redis中取验证码，并与传过来的比较
		String redisCheckCode = stringRedisTemplate.opsForValue().get(phoneKey);
		if (!StringUtils.hasText(redisCheckCode) || redisCheckCode.length() != 6) {
			resetPwdPd.put("status", "7");
			return resetPwdPd;
		}
		if (!checkCode.equals(redisCheckCode)) {
			resetPwdPd.put("status", "5");
			return resetPwdPd;
		}
		newPwd = new SimpleHash(Constants.ALGORITHM_NAME,newPwd,Constants.SHIRO_SALT,Constants.HASH_ITERATIONS).toHex();
		AppUser resetPwdUser = findByPhone(phone);
		if (newPwd.equals(resetPwdUser.getPassword())){
			resetPwdPd.put("status", "8");
			return resetPwdPd;
		}
		resetPwdPd.put("newPwd", newPwd);
		resetPwdPd.put("userName", phone);
		int row = editMyOtherInfo(resetPwdPd);
		stringRedisTemplate.delete(phoneKey);
		resetPwdPd.clear();
		resetPwdPd.put("status", row > 0 ? "0" : "2");
		return resetPwdPd;
	}
	/**
	 * 判断手机号是否已存在
	 * @param userPhone 手机号参数
	 * @return true:存在，false:不存在
	 */
	public boolean isExistsPhone(String userPhone){
		if (!StringUtils.hasText(userPhone)){
			return false;
		}
		PageData phonePd = (PageData)dao.findForObject("WxUserMapper.isExistsPhone", userPhone);
		return phonePd != null && !phonePd.isEmpty();
	}
}